#include "gtest/gtest.h"

#include "bilinkedlist.h"

using namespace algo;

//  test
TEST(TEST1, append)
{
    BiLinkedList<int> list;
    list.append(123);
    std::shared_ptr<int> pval = list.get(0);
    EXPECT_EQ(*pval, 123);
}

//  test
TEST(TEST2, add)
{
    BiLinkedList<int> list;
    list.append(123);
    list.add(0, 234);
    std::shared_ptr<int> p0 = list.get(0);
    std::shared_ptr<int> p1 = list.get(1);
    bool flag = (*p0 == 234) && (*p1 == 123);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST3, size)
{
    BiLinkedList<int> list;
    list.append(0);
    list.add(0, 1);
    list.append(2);
    list.add(1, 3);
    list.append(4);
    list.add(5, 5);
    list.append(6);
    EXPECT_EQ(list.size(), 7);
}

//  test
TEST(TEST4, add)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    std::shared_ptr<int> p0 = list.getFirst();
    std::shared_ptr<int> p1 = list.get(1);
    std::shared_ptr<int> p2 = list.getLast();
    bool flag = (*p0 == 0) && (*p1 == 1);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST5, get_out_of_bounds)
{
    bool flag = false;
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    try
    {
        std::shared_ptr<int> p = list.get(-1);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
        flag = true;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST6, get_out_of_bounds2)
{
    bool flag = false;
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    try
    {
        std::shared_ptr<int> p = list.get(4);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
        flag = true;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST7, remove)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    try
    {
        list.remove(1);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> p = list.getLast();
    bool flag = (list.size() == 2) && (*p == 2);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST8, removeFirst)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    try
    {
        list.removeFirst();
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> p = list.getFirst();
    bool flag = (list.size() == 2) && (*p == 1);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST9, remove)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    try
    {
        list.removeLast();
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> p = list.getLast();
    bool flag = (list.size() == 2) && (*p == 1);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST10, remove_out_of_bounds)
{
     bool flag = false;
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    try
    {
        list.remove(3);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
        flag = true;
    }
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST10, swap)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    list.add(3, 3);
    list.add(4, 4);
    list.add(5, 5);
    try
    {
        list.swap(1, 4);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> p1 = list.get(1);
    std::shared_ptr<int> p4 = list.get(4);
    bool flag = (*p1 == 4) && (*p4 == 1);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST10, swap2)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    list.add(3, 3);
    list.add(4, 4);
    try
    {
        list.swap(1, 4);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> p1 = list.get(1);
    std::shared_ptr<int> pLast = list.getLast();
    bool flag = (*p1 == 4) && (*pLast == 1);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST10, swap3)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    list.add(3, 3);
    try
    {
        list.swap(1, 2);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> p1 = list.get(1);
    std::shared_ptr<int> p2 = list.get(2);
    bool flag = (*p1 == 2) && (*p2 == 1);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST10, swap4)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    list.add(3, 3);
    try
    {
        list.swap(0, 3);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> pFirst = list.getFirst();
    std::shared_ptr<int> pLast = list.getLast();
    bool flag = (*pFirst == 3) && (*pLast == 0);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST10, swap5)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    try
    {
        list.swap(0, 1);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
    std::shared_ptr<int> pFirst = list.getFirst();
    std::shared_ptr<int> pLast = list.getLast();
    bool flag = (*pFirst == 1) && (*pLast == 0);
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST11, iterator)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    BiLinkedList<int>::iterator iter = list.begin();
    *iter = 10;
    std::shared_ptr<int> pFirst = list.getFirst();
    EXPECT_EQ(*pFirst, 10);
}

//  test
TEST(TEST12, iterator)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    BiLinkedList<int>::iterator iter = list.begin();
    ++iter;
    EXPECT_EQ(*iter, 1);
}

//  test
TEST(TEST13, iterator)
{
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    BiLinkedList<int>::iterator iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST14, iterator)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 1);
    list.add(2, 2);
    BiLinkedList<int>::iterator iter = list.rbegin();
    int i = 2;
    bool flag = true;
    while (iter != list.rend())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        --i;
        --iter;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST15, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 3);
    list.add(2, 2);
    list.add(3, 1);
    list.add(4, 4);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(1);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(3);

    list.swap(pX, pY);

    BiLinkedList<int>::iterator iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST16, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 2);
    list.add(2, 1);
    list.add(3, 3);
    list.add(4, 4);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(1);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(2);

    list.swap(pX, pY);

    BiLinkedList<int>::iterator iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST17, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 2);
    list.add(2, 1);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(1);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(2);

    list.swap(pX, pY);

    std::shared_ptr<int> p = list.getLast();
    EXPECT_EQ(*p, 2);
}


//  test
TEST(TEST18, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 1);
    list.add(1, 0);
    list.add(2, 2);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(0);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(1);

    list.swap(pX, pY);

    std::shared_ptr<int> p = list.getFirst();
    EXPECT_EQ(*p, 0);
}


//  test
TEST(TEST19, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 3);
    list.add(2, 2);
    list.add(3, 1);
    list.add(4, 4);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(3);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(1);

    list.swap(pX, pY);

    BiLinkedList<int>::iterator iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST20, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 2);
    list.add(2, 1);
    list.add(3, 3);
    list.add(4, 4);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(2);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(1);

    list.swap(pX, pY);

    BiLinkedList<int>::iterator iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }
    
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST21, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 2);
    list.add(2, 1);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(2);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(1);

    list.swap(pX, pY);

    std::shared_ptr<int> p = list.getLast();
    EXPECT_EQ(*p, 2);
}

//  test
TEST(TEST23, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 1);
    list.add(1, 0);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(1);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(0);

    list.swap(pX, pY);

    std::shared_ptr<int> p = list.getLast();
    EXPECT_EQ(*p, 1);
}

//  test
TEST(TEST22, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 1);
    list.add(1, 0);
    list.add(2, 2);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(1);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(0);

    list.swap(pX, pY);

    std::shared_ptr<int> p = list.getFirst();
    EXPECT_EQ(*p, 0);
}

//  test
TEST(TEST12a, swapNode)
{
    
    BiLinkedList<int> list;
    list.add(0, 1);
    list.add(1, 0);
    list.add(2, 2);
    
    std::shared_ptr<BiLinkedList<int>::BiNode> pX = list.getNode(0);
    std::shared_ptr<BiLinkedList<int>::BiNode> pY = list.getNode(0);

    list.swap(pX, pY);

    std::shared_ptr<int> p = list.getFirst();
    EXPECT_EQ(*p, 1);
}

//  test
TEST(TEST_Sort, sort)
{
    BiLinkedList<int> list;
    
    for (int i = 0; i < 1000; ++i)
    {
        list.add(i, 999 - i);
    }

    list.sort([] (const int &lhs, const int &rhs){  return lhs < rhs;   });

    BiLinkedList<int>::iterator iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }
    if (i != 1000)
    {
        flag = false;
    }
    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST_insert_before, insert_before)
{
    
    BiLinkedList<int> list;
    list.add(0, 1);
    list.add(1, 2);
    list.add(2, 3);
    
    BiLinkedList<int>::iterator iter = list.begin();

    list.insertBefore(iter, 0);

    iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }
    if (i != 4)
    {
        flag = false;
    }

    EXPECT_EQ(flag, true);
}

//  test
TEST(TEST_insert_after, insert_after)
{
    
    BiLinkedList<int> list;
    list.add(0, 0);
    list.add(1, 2);
    list.add(2, 3);
    
    BiLinkedList<int>::iterator iterBegin = list.begin();
    BiLinkedList<int>::iterator iterRbegin = list.rbegin();

    list.insertAfter(iterBegin, 1);
    list.insertAfter(iterRbegin, 4);

    BiLinkedList<int>::iterator iter = list.begin();
    int i = 0;
    bool flag = true;
    while (iter != list.end())
    {
        if (*iter != i)
        {
            flag = false;
            break;
        }
        ++i;
        ++iter;
    }

    iter = list.rbegin();
    int j = 4;
    while (iter != list.rend())
    {
        if (*iter != j)
        {
            flag = false;
            break;
        }
        --j;
        --iter;
    }

    std::shared_ptr<int> pLast = list.getLast();
    if (j == -1 && i != 5 && *pLast == 4)
    {
        flag = false;
    }

    EXPECT_EQ(flag, true);
}

int main(int argc, char **argv)
{
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}